/**
 * Copyright(c)2012 Beijing PeaceMap Co.,Ltd.
 * All right reserved. 
 */
package com.pmc.dwa.common.dao;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.support.lob.LobHandler;

import com.pmc.dwa.common.dialect.Dialect;
import com.pmc.dwa.common.dialect.DialectUtil;
import com.pmc.dwa.common.exception.DaoRuntimeException;
import com.pmc.dwa.common.utils.PaginationSupport;

/**
 * @description 数据库操作类
 * @author aokunsang
 * @date 2013-1-5
 */
public class GenericDao implements IGenericDao {

	protected static Log log = LogFactory.getLog(GenericDao.class);
	private final static int BATCH_COUNT = 50000;   //一次性批量处理条数
	
	protected JdbcTemplate jdbcTemplate;
	protected NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	protected LobHandler lobHandler;
	private Boolean showsql = false;  //是否显示Sql语句
	
	@Override
	public void batchOpeate(String sql, List<Object[]> batchArgs) {
		try {
			if(batchArgs!=null && batchArgs.size()>BATCH_COUNT){
				for(int i=0,length=batchArgs.size();i<length;i+=BATCH_COUNT){  //一次批量处理BATCH_COUNT条记录
					jdbcTemplate.batchUpdate(sql, batchArgs.subList(i, length-i < BATCH_COUNT ? length : i+BATCH_COUNT));
				}
				printSqlInfo(sql,"");
			}else{
				jdbcTemplate.batchUpdate(sql, batchArgs);
				printSqlInfo(sql,batchArgs!=null&&batchArgs.size()<5 ? Arrays.toString(batchArgs.toArray()) : "");
			}
		} catch (Exception e) {
			throw new DaoRuntimeException("----------数据库错误batchOpeate()------", e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> void batchUpdate(String sql, List<T> batchObjs) {
		List<Object> batchArgs = (List<Object>)batchObjs;
		try {
			if(batchArgs!=null && batchArgs.size()>BATCH_COUNT){
				for(int i=0,length=batchArgs.size();i<length;i+=BATCH_COUNT){  //一次批量处理BATCH_COUNT条记录
					namedParameterJdbcTemplate.batchUpdate(sql, 
							SqlParameterSourceUtils.createBatch(
									batchArgs.subList(i, length-i < BATCH_COUNT ? length : i+BATCH_COUNT).toArray()));
				}
			}else{
				namedParameterJdbcTemplate.batchUpdate(sql, SqlParameterSourceUtils.createBatch(batchArgs.toArray()));
			}
			printSqlInfo(sql,"");
		} catch (Exception e) {
			throw new DaoRuntimeException("----------数据库错误batchUpdate()------", e);
		}
	}


	@Override
	public <T> T find(String sql, Class<T> clazz, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.queryForObject(sql, ParameterizedBeanPropertyRowMapper.newInstance(clazz), args);
		}catch(EmptyResultDataAccessException ex){
			return null;
		}catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误find()------", e);
		}
	}


	@Override
	public <T> List<T> query(String sql, Class<T> clazz, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return (List<T>)jdbcTemplate.query(sql,
					ParameterizedBeanPropertyRowMapper.newInstance(clazz),
					args);
		}catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误query()------", e);
		}
	}

	@Override
	public <T> PaginationSupport<T> queryListByRows(String sql,Class<T> clazz, int pageNumber,
			int pageSize,Object...args) {
		String countSql = DialectUtil.getDialectInstance().getCountString(sql);   //总数sql语句
		Map<String,Object> countMap = findMap(countSql,args);
		long totalCount = countMap!=null && !countMap.isEmpty() ? NumberUtils.toLong(countMap.get(Dialect.COUNT_FIELD).toString(),0) : 0;
		//int pageCount = (int)(totalCount + pageSize - 1) / pageSize;
		//pageNumber = pageCount < pageNumber ? pageCount : (pageNumber <= 0 ? 1 : pageNumber);  //当前页码大于总页数，则查询最后一页
		int offset = (pageNumber - 1) * pageSize;                      //计算偏移量
		String fenyeSql = DialectUtil.getDialectInstance().getLimitString(sql, offset, pageSize); //分页sql语句
		List<T> list = query(fenyeSql, clazz, args);
		return new PaginationSupport<T>(list, totalCount, pageNumber,pageSize);
	}
	
	@Override
	public PaginationSupport<Map<String, Object>> queryMaplistByRows(
			String sql, int pageNumber, int pageSize,Object...args) {
		String countSql = DialectUtil.getDialectInstance().getCountString(sql);   //总数sql语句
		Map<String,Object> countMap = findMap(countSql,args);
		long totalCount = countMap!=null && !countMap.isEmpty() ? NumberUtils.toLong(countMap.get(Dialect.COUNT_FIELD).toString(),0) : 0;
		//int pageCount = (int)(totalCount + pageSize - 1) / pageSize;
		//pageNumber = pageCount < pageNumber ? pageCount : (pageNumber <= 0 ? 1 : pageNumber);   //当前页码大于总页数，则查询最后一页
		int offset = (pageNumber - 1) * pageSize;                      //计算偏移量
		String fenyeSql = DialectUtil.getDialectInstance().getLimitString(sql, offset, pageSize); //分页sql语句
		List<Map<String,Object>> list = queryMapList(fenyeSql,args);
		return new PaginationSupport<Map<String, Object>>(list, totalCount, pageNumber,pageSize);
	}

	@Override
	public long stat(String sql, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.queryForLong(sql, args);
		}catch(EmptyResultDataAccessException ex){
			return 0;
		}catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误stat()------", e);
		}
	}


	@Override
	public int update(String sql, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.update(sql, args);
		} catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误update()------", e);
		}
	}
	

	@Override
	public <T> void saveOrUpdate(String sql, T bean) {
		printSqlInfo(sql,"");
		try {
			namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(bean));
		} catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误saveOrUpdate()------", e);
		}
	}
	
	@Override
	public Map<String, Object> findMap(String sql, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.queryForMap(sql, args);
		}catch(EmptyResultDataAccessException ex){
			return new HashMap<String,Object>();
		}catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误findMap()------", e);
		}
	}
	
	@Override
	public List<Map<String, Object>> queryMapList(String sql, Object... args) {
		printSqlInfo(sql,args!=null ? Arrays.toString(args) : "");
		try {
			return jdbcTemplate.queryForList(sql, args);
		} catch (DataAccessException e) {
			throw new DaoRuntimeException("----------数据库错误queryMapList()------", e);
		}
	}
	
	@Override
	public JdbcTemplate getJdbcTemplate() {
//		if(attach){ //如果操作附件，需要注入该类
//			jdbcTemplate.setNativeJdbcExtractor(new CommonsDbcpNativeJdbcExtractor());
//		}
		return jdbcTemplate;
	}


	@Override
	public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
		return namedParameterJdbcTemplate;
	}
	
	/**
	 * 打印sql语句
	 * @param sql
	 * @param parameters
	 */
	protected void printSqlInfo(String sql, String parameters){
		if(showsql){
			System.out.println("Spring JdbcTemplate: " + sql + "；Parameter：" + parameters + "");
		}
	}
	
	/**
	 * 注入数据源[为了实现多数据源，需要在applicationContext-jdbc.xml中配置]
	 * @param dataSource
	 */
	public void setSimpleJdbcTemplate(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
		this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
	}
	/**
	 * 是否显示Sql语句
	 * @param showsql
	 */
	public void setShowsql(Boolean showsql){
		this.showsql = showsql;
	}

	/**
	 * @param lobHandler the lobHandler to set
	 */
	public void setLobHandler(LobHandler lobHandler) {
		this.lobHandler = lobHandler;
	}
}
